#include<sys/stat.h>
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<string.h>
#include<asm/types.h>
#include<linux/netlink.h>
#include<linux/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

#include <linux/netfilter.h>
#include <linux/ip.h>
#include <linux/udp.h>

#include "libipq.h"

#include "bloom_filter.h"
#include "type_routingtable.h"
#include "type_announce.h"
#include "typecast_config.h"

#define MAX_BUFFER_SIZE 1024

int ProcessIPQPacket(struct ipq_handle * pipqHandle)
{
    int ipqRet = 0;
    unsigned char buf[MAX_BUFFER_SIZE];

    // Call ipq_read with negative timeout, this is like a select call.
    ipqRet = ipq_read(pipqHandle, buf, MAX_BUFFER_SIZE, -1);
    if (ipqRet < 0)
    {
        printf("ipq_read failed.\n");
        return -1;
    }
    else if (ipqRet == 0)   // Timeout with no data to read
    {
        printf("ipq_read == 0.\n");
        return 0;
    }
    else // ipqRet > 0      There is data to read
    {
        switch (ipq_message_type(buf))
        {
        case NLMSG_ERROR:
            printf("ipq_message_type = NLMSG_ERROR: %d\n", ipq_get_msgerr(buf));
            return -1;
            break;
        case IPQM_PACKET:
            {
                ipq_packet_msg_t * pMessage = ipq_get_packet(buf);

                if (pMessage->hook == 1) /* 1 == NF_IP_LOCAL_IN */
                {
                    printf("  Caught NF_IP_LOCAL_IN packet\n");
                    struct iphdr * pipHeader = (struct iphdr *) pMessage->payload;

                    u_int32_t srcAddr = pipHeader->saddr;   // This is the source address of packet.
                    u_int32_t dstAddr = pipHeader->daddr;   // This is the multicast destination address.
                    u_int32_t nbrAddr = srcAddr;            // TODO: Figure out which neighbor actually forwarded to us.

                    // We only care about Multicast packets
                    multicast_group_node * targetGroup = FindMulticastGroup(dstAddr);
                    if (targetGroup != NULL)
                    {
                        struct udphdr * pudpHeader = (struct udphdr*) (pMessage->payload + sizeof(struct iphdr));
                        char * packetBloomFilter = (char *) (pudpHeader + sizeof(struct udphdr));

                        // Q: Is routing table local BloomFilter a subfilter of the packet BloomFilter?
                        if (!is_subfilter(packetBloomFilter, targetGroup->LBF))
                        {
                            // No, so drop packet.
                            printf("Local BloomFilter is not a subfilter of packet BloomFilter.  Dropping.\n");
                            ipqRet = ipq_set_verdict(pipqHandle, pMessage->packet_id, NF_DROP, 0, NULL);
                            break;
                        }
                    }

                    // Didn't issue any block, so we accept.
                    ipqRet = ipq_set_verdict(pipqHandle, pMessage->packet_id, NF_ACCEPT, 0, NULL);
                }
                else if (pMessage->hook == 5)    /* 5 == NF_IP_POST_ROUTING */
                {
                    printf("  Caught NF_IP_POST_ROUTING packet\n");
                    struct iphdr * pipHeader = (struct iphdr *) pMessage->payload;

                    u_int32_t srcAddr = pipHeader->saddr;   // This is the source address of packet.
                    u_int32_t dstAddr = pipHeader->daddr;   // This is the multicast destination address.
                    u_int32_t nbrAddr = srcAddr;            // TODO: Figure out which neighbor actually forwarded to us.

                    // We only care about Multicast packets
                    multicast_neighbor_node * targetNeighbor = FindMulticastNeighbor(dstAddr, nbrAddr);
                    time_t now = time(NULL);

                    // Is this neighbor valid and does it have active FBF?
                    if (targetNeighbor != NULL && (targetNeighbor->expire > now))
                    {
                        struct udphdr * pudpHeader = (struct udphdr*) (pMessage->payload + sizeof(struct iphdr));
                        char * packetBloomFilter = (char *) (pudpHeader + sizeof(struct udphdr));

                        // Q: Is routing table local BloomFilter a subfilter of the packet BloomFilter?
                        if (!is_subfilter(packetBloomFilter, targetNeighbor->FBF))
                        {
                            // No, so drop packet.
                            printf("Local BloomFilter is not a subfilter of packet BloomFilter.  Dropping.\n");
                            ipqRet = ipq_set_verdict(pipqHandle, pMessage->packet_id, NF_DROP, 0, NULL);
                            break;
                        }
                    }

                    // Didn't issue any block, so we accept.
                    ipqRet = ipq_set_verdict(pipqHandle, pMessage->packet_id, NF_ACCEPT, 0, NULL);
                }
                else
                {
                    printf("  Caught something else....\n");
                    ipqRet = ipq_set_verdict(pipqHandle, pMessage->packet_id, NF_ACCEPT, 0, NULL);
                }

                if (ipqRet < 0)
                {
                    printf("Failed to ipq_set_verdict.\n");
                    return -1;
                }

                printf("  ipq_set_verdict successful.\n");
                break;
            }
        default:
            printf("ipq_message_type == default.\n");
            return -1;
        }
    }

    return 0;
}

